package com.tianji.learning.service.impl;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tianji.api.cache.CategoryCache;
import com.tianji.api.client.course.CatalogueClient;
import com.tianji.api.client.course.CourseClient;
import com.tianji.api.client.search.SearchClient;
import com.tianji.api.client.user.UserClient;
import com.tianji.api.dto.course.CataSimpleInfoDTO;
import com.tianji.api.dto.course.CourseFullInfoDTO;
import com.tianji.api.dto.course.CourseSimpleInfoDTO;
import com.tianji.api.dto.user.UserDTO;
import com.tianji.common.domain.dto.PageDTO;
import com.tianji.common.exceptions.BadRequestException;
import com.tianji.common.utils.BeanUtils;
import com.tianji.common.utils.CollUtils;
import com.tianji.common.utils.UserContext;
import com.tianji.learning.domain.dto.NoteUpdateDto;
import com.tianji.learning.domain.dto.NotesDto;
import com.tianji.learning.domain.po.GatheredNoes;
import com.tianji.learning.domain.po.Noes;
import com.tianji.learning.domain.query.AdminNotesQuery;
import com.tianji.learning.domain.query.NotesQuery;
import com.tianji.learning.domain.vo.AdminNoteVo;
import com.tianji.learning.domain.vo.AdminNotesPageVo;
import com.tianji.learning.domain.vo.NotesPageVo;
import com.tianji.learning.mapper.NoesMapper;
import com.tianji.learning.service.IGatheredNoesService;
import com.tianji.learning.service.INoesService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author xzh
 * @since 2024-03-27
 */
@Service
@RequiredArgsConstructor
public class NoesServiceImpl extends ServiceImpl<NoesMapper, Noes> implements INoesService {
    private final IGatheredNoesService gatheredNoesService;
    private final UserClient userClient;
    private final SearchClient searchClient;
    private final CourseClient courseClient;
    private final CatalogueClient catalogueClient;
    private final CategoryCache categoryCache;
    private final NoesMapper noesMapper;

    /**
     * 保存学习笔记
     *
     * @param dto
     */
    @Override
    public void saveNote(NotesDto dto) {
        Noes noes = BeanUtils.copyBean(dto, Noes.class);
        noes.setAuthorId(UserContext.getUser());
        save(noes);
    }

    /**
     * 采集笔记
     *
     * @param id
     */
    @Override
    @Transactional
    public void saveGatheredNoteById(Long id) {
        // 未采集则新增采集  采集数+1
        Long userId = UserContext.getUser();
        // 1.增加采集
        GatheredNoes newg = new GatheredNoes();
        newg.setNoesId(id);
        newg.setGatherId(userId);
        newg.setCreateTime(LocalDateTime.now());
        gatheredNoesService.save(newg);

        //2.增加采集数
        noesMapper.saveUsedTimes(id);


    }

    /**
     * 取消采集笔记
     *
     * @param id
     */
    @Override
    @Transactional
    public void removeGatheredNoteById(Long id) {
        //已采集则取消采集 采集数-1
        Long userId = UserContext.getUser();
        GatheredNoes gatheredNoes = gatheredNoesService
                .lambdaQuery()
                .eq(GatheredNoes::getGatherId, userId)
                .eq(GatheredNoes::getNoesId, id)
                .one();
        // 1.删除采集
        gatheredNoesService.removeById(gatheredNoes.getId());
        // 2.减少采集数
        noesMapper.removeUsedTimes(id);
    }

    /**
     * 编辑笔记
     *
     * @param id
     * @param dto
     */
    @Override
    public void updateNoteById(Long id, NoteUpdateDto dto) {
        Noes one = lambdaQuery().eq(Noes::getId, id).one();
        if (one == null) {
            throw new BadRequestException("没有对应笔记,无法修改");
        }
        //修改笔记内容  是否隐私
        lambdaUpdate()
                .eq(Noes::getId, id)
                .set(Noes::getContent, dto.getContent())
                .set(Noes::getIsPrivate, dto.getIsPrivate())
                .update();
    }

    /**
     * 删除笔记
     *
     * @param id
     */
    @Override
    public void removeNoteById(Long id) {
        Noes one = lambdaQuery().eq(Noes::getId, id).one();
        if (one == null) {
            throw new BadRequestException("没有对应笔记,无法删除");
        }
        removeById(id);
    }

    /**
     * 笔记分页查询
     *
     * @param query
     * @return
     */
    @Override
    public PageDTO<NotesPageVo> queryNotesPage(NotesQuery query) {
        return query.getOnlyMine() ? queryMyNotesPage(query) : queryAllNotesPage(query);
    }

    /**
     * 管理端笔记分页查询
     * @param query
     * @return
     */
    @Override
    public PageDTO<AdminNotesPageVo> adminQueryNotesPage(AdminNotesQuery query) {
        List<Long> courseIds=null;
        //1.利用seachClient进行模糊查询
        if (query.getName()!=null){
            courseIds = searchClient.queryCoursesIdByName(query.getName());
            if (CollUtils.isEmpty(courseIds)){
                return PageDTO.empty(0L,0L);
            }
        }
        //2.根据条件查询出所有结果
        Page<Noes> page = lambdaQuery()
                .eq(query.getHidden() != null, Noes::getHidden, query.getHidden())
                .in(courseIds!=null,Noes::getCourseId,courseIds)
                .ge(query.getBeginTime() != null, Noes::getCreateTime, query.getBeginTime())
                .le(query.getEndTime() != null, Noes::getCreateTime, query.getEndTime())
                .page(query.toMpPageDefaultSortByCreateTimeDesc());

        List<Noes> records = page.getRecords();
        if (CollUtils.isEmpty(records)){
            return PageDTO.empty(page);
        }

        //3.查询出作者姓名
        List<Long> userIds = records.stream().map(Noes::getAuthorId).collect(Collectors.toList());
        List<UserDTO> userDTOS = userClient.queryUserByIds(userIds);
        Map<Long,String> userMap=null;
        if (CollUtils.isNotEmpty(userDTOS)){
            userMap=userDTOS.stream().collect(Collectors.toMap(UserDTO::getId,UserDTO::getName));
        }

        //4.查出课程章节名
        //4.1课程名
        Set<Long> courseSet = records.stream().map(Noes::getCourseId).collect(Collectors.toSet());
        List<CourseSimpleInfoDTO> simpleInfoList = courseClient.getSimpleInfoList(courseSet);
        Map<Long,String> courseMap=null;
        if (CollUtils.isNotEmpty(simpleInfoList)){
            courseMap=simpleInfoList.stream().collect(Collectors
                    .toMap(CourseSimpleInfoDTO::getId,CourseSimpleInfoDTO::getName));
        }

        //4.2章名
        Set<Long> chapterSet = records.stream().map(Noes::getChapterId).collect(Collectors.toSet());
        List<CataSimpleInfoDTO> cataSimpleInfoDTOS = catalogueClient.batchQueryCatalogue(chapterSet);
        Map<Long,String> chapterMap=null;
        if (CollUtils.isNotEmpty(cataSimpleInfoDTOS)){
            chapterMap=cataSimpleInfoDTOS.stream().collect(Collectors
                    .toMap(CataSimpleInfoDTO::getId,CataSimpleInfoDTO::getName));
        }

        //4.3节名
        Set<Long> sectionSet = records.stream().map(Noes::getSectionId).collect(Collectors.toSet());
        List<CataSimpleInfoDTO> cataSimpleInfoDTOS2 = catalogueClient.batchQueryCatalogue(sectionSet);
        Map<Long,String> sectionMap=null;
        if (CollUtils.isNotEmpty(cataSimpleInfoDTOS2)){
            sectionMap=cataSimpleInfoDTOS2.stream().collect(Collectors
                    .toMap(CataSimpleInfoDTO::getId,CataSimpleInfoDTO::getName));
        }

        //5.封装返回
        List<AdminNotesPageVo> list=new ArrayList<>(records.size());
        for (Noes r : records) {
            AdminNotesPageVo vo= BeanUtils.copyBean(r, AdminNotesPageVo.class);
            if (CollUtils.isNotEmpty(userMap)) {
                vo.setAuthorName(userMap.get(r.getAuthorId()));
            }
            if (CollUtils.isNotEmpty(courseMap)){
                vo.setCourseName(courseMap.get(r.getCourseId()));
            }
            if (CollUtils.isNotEmpty(chapterMap)){
                vo.setChapterName(chapterMap.get(r.getChapterId()));
            }
            if (CollUtils.isNotEmpty(sectionMap)){
                vo.setSectionName(sectionMap.get(r.getSectionId()));
            }
            list.add(vo);
        }
        return PageDTO.of(page,list);
    }

    /**
     * 管理端笔记详情
     * @param id
     * @return
     */
    @Override
    public AdminNoteVo adminQueryNote(Long id) {
        //1.根据id获得笔记详情
        Noes noes = lambdaQuery().eq(Noes::getId, id).one();
        if (noes == null){
            throw new BadRequestException("笔记不存在");
        }
        //拷贝
        AdminNoteVo vo = BeanUtils.copyBean(noes, AdminNoteVo.class);
        //2.获得作者详情
        UserDTO userDTO = userClient.queryUserById(noes.getAuthorId());
        if (userDTO!=null){
            vo.setAuthorName(userDTO.getName());
            vo.setAuthorPhone(userDTO.getCellPhone());
        }
        //3.获得采集人详情
        List<GatheredNoes> gatheredNoesList = gatheredNoesService
                .lambdaQuery().eq(GatheredNoes::getNoesId, id).list();

        List<String> list=new ArrayList<>(gatheredNoesList.size());
        if (CollUtils.isNotEmpty(gatheredNoesList)){
            List<Long> gatherIds = gatheredNoesList.stream()
                    .map(GatheredNoes::getGatherId).collect(Collectors.toList());
            List<UserDTO> userDTOS = userClient.queryUserByIds(gatherIds);
            userDTOS.forEach(u->list.add(u.getName()));
        }
        vo.setGathers(list);

        //4.获得课程章节名称
        CourseFullInfoDTO courseInfoById = courseClient
                .getCourseInfoById(noes.getCourseId(), false, false);
        if (courseInfoById!=null){
            vo.setCourseName(courseInfoById.getName());

            //分类
            String categoryNames = categoryCache.getCategoryNames(courseInfoById.getCategoryIds());
            vo.setCategoryNames(categoryNames);
        }

        //章节
        List<Long> ids=new ArrayList<>(2);
        ids.add(noes.getChapterId());
        ids.add(noes.getSectionId());
        List<CataSimpleInfoDTO> cataSimpleInfoDTOS = catalogueClient.batchQueryCatalogue(ids);
        if (CollUtils.isNotEmpty(cataSimpleInfoDTOS)){
            Map<Long, String> map = cataSimpleInfoDTOS.stream()
                    .collect(Collectors.toMap(CataSimpleInfoDTO::getId, CataSimpleInfoDTO::getName));
            vo.setChapterName(map.get(noes.getChapterId()));
            vo.setSectionName(map.get(noes.getSectionId()));
        }

        return vo;
    }

    /**
     * 隐藏或显示笔记
     * @param id
     * @param hidden
     */
    @Override
    public void hiddenOrNoHiddenNote(Long id, Boolean hidden) {
        lambdaUpdate().eq(Noes::getId,id).set(Noes::getHidden,hidden).update();
    }

    /**
     * 笔记分页查询 查所有
     *
     * @param query
     * @return
     */
    private PageDTO<NotesPageVo> queryAllNotesPage(NotesQuery query) {
        // 逻辑: 查出所有笔记 隐私的不显示 隐藏的不显示
        //1.查询符合结果的笔记
        Page<Noes> page = lambdaQuery()
                .eq(query.getCourseId() != null, Noes::getCourseId, query.getCourseId())
                .eq(query.getSectionId() != null, Noes::getSectionId, query.getSectionId())
                .eq(Noes::getHidden, false)
                .eq(Noes::getIsPrivate, false)
                .page(query.toMpPageDefaultSortByCreateTimeDesc());
        List<Noes> records = page.getRecords();
        if (CollUtils.isEmpty(records)) {
            return PageDTO.empty(page);
        }
        //2.获得用户名称头像
        //2.1得到用户id集合
        List<Long> userIds = records.stream().map(Noes::getAuthorId).collect(Collectors.toList());
        //2.2查询出所有用户名称头像
        List<UserDTO> userDTOS = userClient.queryUserByIds(userIds);
        if (CollUtils.isEmpty(userDTOS)) {
            return PageDTO.empty(page);
        }
        Map<Long, UserDTO> map = userDTOS.stream().collect(Collectors.toMap(UserDTO::getId, u -> u));
        //3.判断当前用户是否对笔记进行收集
        //3.1获得所有笔记id集合
        Long userId = UserContext.getUser();
        List<Long> noteIds = records.stream().map(Noes::getId).collect(Collectors.toList());
        List<GatheredNoes> gatheredNoesList = gatheredNoesService
                .lambdaQuery()
                .eq(GatheredNoes::getGatherId, userId)
                .in(GatheredNoes::getNoesId, noteIds)
                .list();
        //4.封装
        List<NotesPageVo> list = new ArrayList<>(records.size());
        List<Long> collect = null;
        if (CollUtils.isNotEmpty(gatheredNoesList)) {
            collect = gatheredNoesList.stream()
                    .map(GatheredNoes::getNoesId).collect(Collectors.toList());
        }
        for (Noes n : records) {
            NotesPageVo vo = BeanUtils.copyBean(n, NotesPageVo.class);
            vo.setAuthorIcon(map.get(n.getAuthorId()).getIcon());
            vo.setAuthorName(map.get(n.getAuthorId()).getName());
            if (collect != null && collect.size() > 0) {
                vo.setIsGathered(collect.contains(n.getId()));
            } else {
                vo.setIsGathered(false);
            }
            list.add(vo);
        }
        return PageDTO.of(page, list);
    }

    /**
     * 笔记分页查询 查我的
     *
     * @param query
     * @return
     */
    private PageDTO<NotesPageVo> queryMyNotesPage(NotesQuery query) {
        // 逻辑: 查出所有笔记 显示是否隐私 隐藏的不显示
        //1.查询符合结果的笔记
        Long userId = UserContext.getUser();
        Page<Noes> page = lambdaQuery()
                .eq(query.getCourseId() != null, Noes::getCourseId, query.getCourseId())
                .eq(query.getSectionId() != null, Noes::getSectionId, query.getSectionId())
                .eq(Noes::getHidden, false)
                .eq(Noes::getAuthorId, userId)
                .page(query.toMpPageDefaultSortByCreateTimeDesc());
        List<Noes> records = page.getRecords();
        if (CollUtils.isEmpty(records)) {
            return PageDTO.empty(page);
        }
        //2.获得用户名称头像
        UserDTO userDTO = userClient.queryUserById(userId);
        //4.封装
        List<NotesPageVo> list = new ArrayList<>(records.size());
        for (Noes n : records) {
            NotesPageVo vo = BeanUtils.copyBean(n, NotesPageVo.class);
            vo.setAuthorIcon(userDTO.getIcon());
            vo.setAuthorName(userDTO.getName());
            vo.setIsGathered(false);
            list.add(vo);
        }
        return PageDTO.of(page, list);
    }


}
